﻿using System;
using System.Drawing;
using System.Runtime.InteropServices;

namespace Hardcodet.Wpf.TaskbarNotification.Interop
{
	/// <summary>
	/// Resolves the current tray position.
	/// </summary>
	static public class TrayInfo
	{
		/// <summary>
		/// Gets the position of the system tray.
		/// </summary>
		/// <returns>Tray coordinates.</returns>
		static public Point GetTrayLocation()
		{
			var info = new AppBarInfo();
			info.GetSystemTaskBarPosition();

			Rectangle rcWorkArea = info.WorkArea;

			int x = 0, y = 0;
			if (info.Edge == AppBarInfo.ScreenEdge.Left)
			{
				x = rcWorkArea.Left + 2;
				y = rcWorkArea.Bottom;
			}
			else if (info.Edge == AppBarInfo.ScreenEdge.Bottom)
			{
				x = rcWorkArea.Right;
				y = rcWorkArea.Bottom;
			}
			else if (info.Edge == AppBarInfo.ScreenEdge.Top)
			{
				x = rcWorkArea.Right;
				y = rcWorkArea.Top;
			}
			else if (info.Edge == AppBarInfo.ScreenEdge.Right)
			{
				x = rcWorkArea.Right;
				y = rcWorkArea.Bottom;
			}

			return new Point {X = x, Y = y};
		}
	}

	internal class AppBarInfo
	{
		#region ScreenEdge enum

		public enum ScreenEdge
		{
			Undefined = -1,
			Left = ABE_LEFT,
			Top = ABE_TOP,
			Right = ABE_RIGHT,
			Bottom = ABE_BOTTOM
		}

		#endregion

		private const int ABE_BOTTOM = 3;

		private const int ABE_LEFT = 0;

		private const int ABE_RIGHT = 2;

		private const int ABE_TOP = 1;

		private const int ABM_GETTASKBARPOS = 0x00000005;

		// SystemParametersInfo constants
		private const UInt32 SPI_GETWORKAREA = 0x0030;

		private APPBARDATA m_data;

		public ScreenEdge Edge
		{
			get { return (ScreenEdge)this.m_data.uEdge; }
		}

		public Rectangle WorkArea
		{
			get
			{
				Int32 bResult = 0;
				var rc = new RECT();
				IntPtr rawRect = Marshal.AllocHGlobal(Marshal.SizeOf(rc));
				bResult = SystemParametersInfo(SPI_GETWORKAREA, 0, rawRect, 0);
				rc = (RECT)Marshal.PtrToStructure(rawRect, rc.GetType());

				if (bResult == 1)
				{
					Marshal.FreeHGlobal(rawRect);
					return new Rectangle(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
				}

				return new Rectangle(0, 0, 0, 0);
			}
		}

		[DllImport("user32.dll")]
		static private extern IntPtr FindWindow(String lpClassName, String lpWindowName);

		[DllImport("shell32.dll")]
		static private extern UInt32 SHAppBarMessage(UInt32 dwMessage, ref APPBARDATA data);

		[DllImport("user32.dll")]
		static private extern Int32 SystemParametersInfo(UInt32 uiAction, UInt32 uiParam, IntPtr pvParam, UInt32 fWinIni);

		public void GetPosition(string strClassName, string strWindowName)
		{
			this.m_data = new APPBARDATA();
			this.m_data.cbSize = (UInt32)Marshal.SizeOf(this.m_data.GetType());

			IntPtr hWnd = FindWindow(strClassName, strWindowName);

			if (hWnd != IntPtr.Zero)
			{
				UInt32 uResult = SHAppBarMessage(ABM_GETTASKBARPOS, ref this.m_data);

				if (uResult != 1)
					throw new Exception("Failed to communicate with the given AppBar");
			}
			else
				throw new Exception("Failed to find an AppBar that matched the given criteria");
		}

		public void GetSystemTaskBarPosition()
		{
			this.GetPosition("Shell_TrayWnd", null);
		}

		#region Nested type: APPBARDATA

		[StructLayout(LayoutKind.Sequential)]
		private struct APPBARDATA
		{
			public UInt32 cbSize;

			public readonly IntPtr hWnd;

			public readonly UInt32 uCallbackMessage;

			public readonly UInt32 uEdge;

			public readonly RECT rc;

			public readonly Int32 lParam;
		}

		#endregion

		#region Nested type: RECT

		[StructLayout(LayoutKind.Sequential)]
		private struct RECT
		{
			public readonly Int32 left;

			public readonly Int32 top;

			public readonly Int32 right;

			public readonly Int32 bottom;
		}

		#endregion
	}
}